// ===============================================
// core_shape_painter
//
// Create by Will on 2024/4/12 22:45
// Copyright @Will.All rights reserved.
// ===============================================

import 'dart:math';

import 'package:dash_painter/dash_painter.dart';
import 'package:flutter/material.dart';

class CoreShapePainter extends CustomPainter {
  CoreShapePainter({required this.n});
  final int n;

  @override
  void paint(Canvas canvas, Size size) {
    canvas.translate(size.width / 2, size.height / 2);
    final count = n;
    const radius = 140 / 2;
    drawAxis(canvas, size);
    final points = <Offset>[];
    const rotate = -pi / 2;
    for (var i = 0; i < count; i++) {
      final perRad = 2 * pi / count * i;
      points.add(Offset(
        radius * cos(perRad + rotate),
        radius * sin(perRad + rotate),
      ));
    }
    _drawShapeHelper(canvas, points, radius);
    _drawShape(canvas, points);
  }

  final Paint shapePaint = Paint()..style = PaintingStyle.stroke;

  void _drawShape(Canvas canvas, List<Offset> points) {
    final shapePath = Path();
    shapePath.moveTo(points[0].dx, points[0].dy);
    for (var i = 1; i < points.length; i++) {
      shapePath.lineTo(points[i].dx, points[i].dy);
    }
    shapePath.close();
    canvas.drawPath(shapePath, shapePaint);
  }

  void _drawShapeHelper(Canvas canvas, List<Offset> points, double radius) {
    final helpPath = Path()
      ..addOval(Rect.fromCenter(
          center: Offset.zero, width: radius * 2, height: radius * 2));
    dashPainter.paint(canvas, helpPath, helpPaint);
    for (final point in points) {
      drawAnchor(canvas, point);
    }
  }

  final Paint pointPaint = Paint()
    ..style = PaintingStyle.stroke
    ..strokeWidth = 1;

  void drawAnchor(Canvas canvas, Offset offset) {
    canvas
      ..drawCircle(offset, 4, pointPaint..style = PaintingStyle.stroke)
      ..drawCircle(offset, 2, pointPaint..style = PaintingStyle.fill);
  }

  @override
  bool shouldRepaint(covariant CoreShapePainter oldDelegate) {
    return oldDelegate.n != n;
  }

  final Paint helpPaint = Paint()
    ..style = PaintingStyle.stroke
    ..color = Colors.lightBlue
    ..strokeWidth = 1;

  final DashPainter dashPainter = const DashPainter(span: 4, step: 4);

  Paint axisPaint = Paint()..color = Colors.lightBlue;

  void drawAxis(Canvas canvas, Size size) {
    axisPaint.style = PaintingStyle.stroke;
    final helpPath = Path()
      ..moveTo(-size.width / 2, 0)
      ..relativeLineTo(size.width, 0)
      ..moveTo(0, -size.height / 2)
      ..relativeLineTo(0, size.height);
    dashPainter.paint(canvas, helpPath, axisPaint);

    axisPaint.style = PaintingStyle.fill;
    final arrowXPath = Path()
      ..moveTo(size.width / 2, 0)
      ..relativeLineTo(-8, 4)
      ..relativeLineTo(0, -8)
      ..close();
    canvas.drawPath(arrowXPath, axisPaint);

    final arrowYPath = Path()
      ..moveTo(0, size.height / 2)
      ..relativeLineTo(4, -8)
      ..relativeLineTo(-8, 0)
      ..close();
    canvas.drawPath(arrowYPath, axisPaint);

    drawHelpText('x', canvas, Offset(size.width / 2 - 8, 2));
    drawHelpText('y', canvas, Offset(6, size.height / 2 - 15));
  }

  final TextPainter textPainter = TextPainter(
    textAlign: TextAlign.center,
    textDirection: TextDirection.ltr,
  );

  void drawHelpText(
    String text,
    Canvas canvas,
    Offset offset, {
    Color color = Colors.lightBlue,
  }) {
    textPainter
      ..text = TextSpan(
        text: text,
        style: TextStyle(fontSize: 11, color: color),
      )
      ..layout(maxWidth: 200)
      ..paint(canvas, offset);
  }
}

class CoreShapePath {
  CoreShapePath.star({
    this.n = 5,
    this.outRadius = 100,
    this.innerRadius = 60,
  });

  CoreShapePath.polygon({
    this.n = 5,
    this.outRadius = 100,
  }) : innerRadius = null;

  final int n;
  final double outRadius;
  final double? innerRadius;
  Path? _path;

  Path get path {
    if (_path == null) {
      _buildPath();
    }
    return _path!;
  }

  void _buildPath() {
    final count = n;
    final offset = pi / count;
    final points = <Offset>[];
    const rotate = -pi / 2;
    for (var i = 0; i < count; i++) {
      final perRad = 2 * pi / count * i;
      points.add(Offset(
        outRadius * cos(perRad + rotate),
        outRadius * sin(perRad + rotate),
      ));
      if (innerRadius != null) {
        points.add(Offset(
          innerRadius! * cos(perRad + rotate + offset),
          innerRadius! * sin(perRad + rotate + offset),
        ));
      }
    }

    _path = Path();
    _path!.moveTo(points[0].dx, points[0].dy);
    for (var i = 1; i < points.length; i++) {
      _path!.lineTo(points[i].dx, points[i].dy);
    }
    _path!.close();
  }
}

class CoreShapeBorder extends ShapeBorder {
  @override
  EdgeInsetsGeometry get dimensions => EdgeInsets.zero;

  @override
  Path getInnerPath(Rect rect, {TextDirection? textDirection}) {
    return Path();
  }

  @override
  Path getOuterPath(Rect rect, {TextDirection? textDirection}) {
    final shapePath = CoreShapePath.polygon(
      n: 6,
      outRadius: rect.shortestSide / 2,
    );
    return shapePath.path
        .shift(Offset(rect.longestSide / 2, rect.shortestSide / 2));
  }

  @override
  void paint(Canvas canvas, Rect rect, {TextDirection? textDirection}) {}

  @override
  ShapeBorder scale(double t) {
    return this;
  }
}
